-module(config2erl).

-include("common.hrl").

-export([beam/6]).

%%======================EXPORTED FUNCTION==================================
%% 生成beam文件
beam(SrcFile, ModAtom, OutDir, Type, TransformFun,MD5) ->
	case file:consult(SrcFile) of
		{ok, Terms} ->
			beam2(Terms, ModAtom, OutDir, Type, TransformFun,MD5);
		Error ->
			?ERR("read config file ModAtom=~p, error=~p, file=~s",[ModAtom, Error, SrcFile]),
			exit(Error)
	end.

%%======================LOCAL FUNCTION==================================
%% @doc 将策划配置的TermList转化成程序需要的TermList，然后 将record List和key_value List标准化
unique(Terms0, TransformFun) ->
	Terms = 
		if is_function(TransformFun, 1) ->
			   TransformFun(Terms0);
		   true ->
			   Terms0
		end,
	KeyValList = 
		case lists:all(fun(E) -> tuple_size(E) =:= 2 end, Terms) of
			true ->
				Terms;
			false ->
				[{element(2, Tuple),Tuple} || Tuple <- Terms]
		end,
	Dict = dict:from_list(KeyValList),
	case dict:size(Dict) =:= length(KeyValList) of
		true ->
			next;
		false ->
			DuplicateList = KeyValList -- dict:to_list(Dict),
			?ERR("duplicate_config_key DuplicateList=~p",[DuplicateList]),
			throw({duplicate_config_key,DuplicateList})
	end,
	KeyValList.
	
%% key_value方式生成代码
gen_src(ModStr, KeysList, Terms, key_value,MD5) ->
Tail = 
"get(_Key) -> undefined.
",
    FunctionClause = lists:foldl(fun({Key, Value}, C) ->
                                lists:flatten(io_lib:format("get(~w) -> ~w;\n", [Key, Value])) ++ C
                        end,
                        Tail,
                        lists:reverse(Terms)),
    StrList = lists:flatten(io_lib:format("     ~w\n", [KeysList])),
"
-module(" ++ ModStr ++ ").
-export([get_list/0,get/1,get_md5/0]).

get_md5()-><<"++ lists:foldl(fun(N,AccStr) -> ?IF(AccStr=="",erlang:integer_to_list(N),erlang:integer_to_list(N)++","++AccStr) end, "", lists:reverse(erlang:binary_to_list(MD5))) ++">>.
get_list()->"++ StrList ++".\n\n" ++ FunctionClause;

%% if_clause方式生成代码
gen_src(ModStr, KeysList, Terms, if_clause,MD5) ->
Tail = "\t\ttrue ->undefined\n\tend.
",
    IfClause = lists:foldl(fun({Key, Value}, C) ->
                                lists:flatten(io_lib:format("\t\tKey >=  ~w -> ~w;\n", [Key, Value])) ++ C
                        end,
                        Tail,
                        Terms),
    StrList = lists:flatten(io_lib:format("     ~w\n", [KeysList])),
	Min = lists:flatten(io_lib:format("\nget_min()->~w.\n",[hd(KeysList)])),
	Max = lists:flatten(io_lib:format("\nget_max()->~w.\n",[lists:last(KeysList)])),

"
-module(" ++ ModStr ++ ").
-export([get_list/0,get/1,get_min/0,get_max/0,get_md5/0]).

get_md5()-><<"++ lists:foldl(fun(N,AccStr) -> ?IF(AccStr=="",erlang:integer_to_list(N),erlang:integer_to_list(N)++","++AccStr) end, "", lists:reverse(erlang:binary_to_list(MD5))) ++">>.
get_list()->"++ StrList ++".\n\n"++Min ++Max++ "get(Key) -> \n\tif\n" ++IfClause.

beam2(Terms0, ModAtom, OutDir, Type, TransformFun,MD5) ->
	KeyValueList = unique(Terms0, TransformFun),
	KeysList = [Key || {Key,_Value} <- KeyValueList],
	ModStr = atom_to_list(ModAtom),
    try
        SrcCode = gen_src(ModStr, KeysList, KeyValueList, Type,MD5),
		{ModAtom, CodeBin} = dynamic_compile:from_string( SrcCode ),
		OutFile = filename:join([OutDir,ModStr++".beam"]),
		write_file(OutFile, CodeBin),
        {ok, CodeBin}
    catch
        Type:Reason:Trace ->
            ?ERR("Error compiling ~1000000000p: Type=~w,Reason=~1000000000p,Trace=~1000000000p,~n", [ModAtom, Type, Reason,Trace ]),
			{error, {Type, Reason}}
    end.

write_file(File, Bin) ->
%%	FileNameTmp = File++".tmp",
	file:write_file(File, Bin, [write]).
%%	file:rename(FileNameTmp, File).